enum AudioPermissionErrorType {
    NO_USER_INTERACTION_ERROR = 'NoUserInteractionError',
    AUTOPLAY_BLOCKED = 'AutoplayBlocked',
    AUTOPLAY_UNKNOWN_ERROR = 'AutoplayUnknownError',
    SECURE_CONTEXT_ERROR = 'SecureContextError',
    PERMISSION_DENIED_PERMANENTLY = 'PermissionDeniedPermanently',
    NOT_ALLOWED_ERROR = 'NotAllowedError',
    MEDIA_DEVICES_API_UNAVAILABLE = 'MediaDevicesApiUnavailable',
    NOT_FOUND_ERROR = 'NotFoundError',
    NOT_READABLE_ERROR = 'NotReadableError',
    OVERCONSTRAINED_ERROR = 'OverconstrainedError',
    SECURITY_ERROR = 'SecurityError',
    MICROPHONE_UNKNOWN_ERROR = 'MicrophoneUnknownError',
    MICROPHONE_TIMEOUT_ERROR = 'MicrophoneTimeoutError',
}

class AudioPermissionError extends Error {
    public name: AudioPermissionErrorType

    private constructor(name: AudioPermissionErrorType, message: string) {
        super(message)
        this.name = name
    }

    /**
     * 创建音频权限错误实例
     * @param name - 错误类型
     * @param message - 自定义错误消息
     * @returns 音频权限错误实例
     */
    public static create(
        name: AudioPermissionErrorType,
        message?: string,
    ): AudioPermissionError {
        const codeMessageMap = {
            [AudioPermissionErrorType.NO_USER_INTERACTION_ERROR]: '需要用户交互才能播放音频',
            [AudioPermissionErrorType.AUTOPLAY_BLOCKED]: '自动播放被浏览器阻止',
            [AudioPermissionErrorType.AUTOPLAY_UNKNOWN_ERROR]: '未知自动播放错误',
            [AudioPermissionErrorType.SECURE_CONTEXT_ERROR]: '麦克风权限需要在安全上下文（HTTPS）中使用',
            [AudioPermissionErrorType.PERMISSION_DENIED_PERMANENTLY]: '音频权限已被永久拒绝，需在浏览器设置中开启麦克风权限',
            [AudioPermissionErrorType.MEDIA_DEVICES_API_UNAVAILABLE]: 'MediaDevices API不可用',
            [AudioPermissionErrorType.NOT_ALLOWED_ERROR]: '麦克风权限被拒绝',
            [AudioPermissionErrorType.NOT_READABLE_ERROR]: '音频设备被其他应用程序占用',
            [AudioPermissionErrorType.NOT_FOUND_ERROR]: '未找到音频输入设备',
            [AudioPermissionErrorType.SECURITY_ERROR]: '安全策略阻止访问麦克风',
            [AudioPermissionErrorType.OVERCONSTRAINED_ERROR]: '音频设备不满足指定的约束条件',
            [AudioPermissionErrorType.MICROPHONE_UNKNOWN_ERROR]: '未知麦克风权限错误',
            [AudioPermissionErrorType.MICROPHONE_TIMEOUT_ERROR]: '获取麦克风权限超时，请检查设备权限设置或重试',
        }

        return new AudioPermissionError(name, message || codeMessageMap[name as keyof typeof codeMessageMap])
    }
}

/**
 * 音频播放权限管理类
 */
class AudioPlaybackPermission {
    private userInteracted: boolean = false
    private executeContext!: AudioPermission

    /**
     * 构造函数
     */
    constructor() {
        this.initPlaybackPermission()
    }

    /**
     * 初始化播放权限检测
     */
    private initPlaybackPermission(): void {
        // PC端预检测AudioContext状态
        this.checkInitialAudioContextState().catch(() => {
            this.setupUserInteractionListeners()
        })
    }

    /**
     * 检测初始AudioContext状态
     */
    private async checkInitialAudioContextState(): Promise<void> {
        const testContext = new (window.AudioContext || (window as any).webkitAudioContext)()

        if (testContext.state === 'running') {
            this.userInteracted = true
        }

        await testContext.close()

        if (this.userInteracted) {
            return Promise.resolve()
        } else {
            return Promise.reject()
        }
    }

    /**
     * 设置用户交互监听器
     */
    private setupUserInteractionListeners(): void {
        const interactionEvents = [
            'click',
            'keydown',
            'mousedown',
            'pointerdown',
        ]

        const handleInteraction = (): void => {
            this.userInteracted = true

            interactionEvents.forEach((eventType: string) => {
                document.removeEventListener(eventType, handleInteraction)
            })
        }

        interactionEvents.forEach((eventType) => {
            document.addEventListener(eventType, handleInteraction, { passive: true, once: true })
        })
    }

    /**
     * 请求播放权限
     */
    public async requestPlaybackPermission(): Promise<void> {
        let audioContext: AudioContext | null = null
        try {
            if (!this.userInteracted) {
                throw AudioPermissionError.create(AudioPermissionErrorType.NO_USER_INTERACTION_ERROR)
            }

            audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()

            if (audioContext.state !== 'running') {
                throw AudioPermissionError.create(AudioPermissionErrorType.AUTOPLAY_BLOCKED)
            }

            return Promise.resolve()
        } catch (error) {
            if (error instanceof AudioPermissionError) {
                return Promise.reject(error)
            }

            const unknownError = AudioPermissionError.create(
                AudioPermissionErrorType.AUTOPLAY_UNKNOWN_ERROR,
                error instanceof Error ? error.message : undefined,
            )
            return Promise.reject(unknownError)
        } finally {
            if (audioContext && audioContext.state !== 'closed') {
                try {
                    await audioContext.close()
                } catch { }
            }
        }
    }

    /**
     * 执行上下文设置
     * @param context 执行上下文
     */
    setExecuteContext(context: AudioPermission): void {
        this.executeContext = context
    }
}

/**
 * 麦克风权限管理类
 */
class MicrophonePermission {
    private microphonePermissionGranted: boolean = false
    private permissionDeniedPermanently: boolean = false

    /**
     * 检查是否在安全上下文中
     */
    private isSecureContext(): boolean {
        return window.isSecureContext || location.protocol === 'https:' || location.hostname === 'localhost'
    }

    /**
     * 带超时的getUserMedia调用
     * @param constraints - 媒体约束
     * @param timeoutMs - 超时时间（毫秒）
     */
    private async getUserMediaWithTimeout(
        constraints: MediaStreamConstraints,
        timeoutMs: number = 10000,
    ): Promise<MediaStream> {
        return new Promise((resolve, reject) => {
            const timeoutId = setTimeout(() => {
                reject(AudioPermissionError.create(
                    AudioPermissionErrorType.MICROPHONE_TIMEOUT_ERROR,
                ))
            }, timeoutMs)

            navigator.mediaDevices.getUserMedia(constraints)
                .then((stream) => {
                    clearTimeout(timeoutId)
                    resolve(stream)
                })
                .catch((error) => {
                    clearTimeout(timeoutId)
                    reject(error)
                })
        })
    }

    /**
     * 请求麦克风权限
     */
    public async requestMicrophonePermission(): Promise<void> {
        try {
            // 快速检查权限状态
            if (navigator.permissions) {
                try {
                    const result = await navigator.permissions.query({ name: 'microphone' as PermissionName })
                    if (result.state === 'granted') {
                        this.microphonePermissionGranted = true
                        return Promise.resolve()
                    } else if (result.state === 'denied') {
                        this.permissionDeniedPermanently = true
                        throw AudioPermissionError.create(AudioPermissionErrorType.PERMISSION_DENIED_PERMANENTLY)
                    }
                } catch { }
            }

            if (!this.isSecureContext()) {
                throw AudioPermissionError.create(
                    AudioPermissionErrorType.SECURE_CONTEXT_ERROR,
                )
            }

            if (this.permissionDeniedPermanently) {
                throw AudioPermissionError.create(AudioPermissionErrorType.PERMISSION_DENIED_PERMANENTLY)
            }

            if (this.microphonePermissionGranted) {
                return Promise.resolve()
            }

            if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
                throw AudioPermissionError.create(
                    AudioPermissionErrorType.MEDIA_DEVICES_API_UNAVAILABLE,
                )
            }

            const stream = await this.getUserMediaWithTimeout({
                audio: {
                    echoCancellation: true,
                    noiseSuppression: true,
                    autoGainControl: true,
                },
            })

            this.microphonePermissionGranted = true

            stream.getTracks().forEach((track) => {
                track.stop()
            })

            return Promise.resolve()
        } catch (error) {
            this.microphonePermissionGranted = false

            if (error instanceof AudioPermissionError) {
                return Promise.reject(error)
            }

            if (error instanceof DOMException) {
                let errorType: AudioPermissionErrorType

                switch (error.name) {
                    case 'NotFoundError':
                        errorType = AudioPermissionErrorType.NOT_FOUND_ERROR
                        break
                    case 'NotAllowedError':
                        this.permissionDeniedPermanently = true
                        errorType = AudioPermissionErrorType.NOT_ALLOWED_ERROR
                        break
                    case 'NotReadableError':
                        errorType = AudioPermissionErrorType.NOT_READABLE_ERROR
                        break
                    case 'OverconstrainedError':
                        errorType = AudioPermissionErrorType.OVERCONSTRAINED_ERROR
                        break
                    case 'SecurityError':
                        errorType = AudioPermissionErrorType.SECURITY_ERROR
                        break
                    default:
                        errorType = AudioPermissionErrorType.MICROPHONE_UNKNOWN_ERROR
                }

                const audioError = AudioPermissionError.create(errorType)
                return Promise.reject(audioError)
            }

            const unknownError = AudioPermissionError.create(
                AudioPermissionErrorType.MICROPHONE_UNKNOWN_ERROR,
                error instanceof Error ? error.message : undefined,
            )
            return Promise.reject(unknownError)
        }
    }
}

/**
 * 统一的音频权限管理类
 */
class AudioPermission {
    public playbackPermission: AudioPlaybackPermission
    public microphonePermission: MicrophonePermission

    /**
     * 构造函数
     */
    constructor() {
        this.playbackPermission = new AudioPlaybackPermission()
        this.microphonePermission = new MicrophonePermission()

        this.playbackPermission.setExecuteContext(this)
    }

    /**
     * 请求音频播放权限
     */
    public async requestPlaybackPermission(): Promise<void> {
        return this.playbackPermission.requestPlaybackPermission()
    }

    /**
     * 请求麦克风权限
     */
    public async requestMicrophonePermission(): Promise<void> {
        return this.microphonePermission.requestMicrophonePermission()
    }

}

let audioPermissionInstance: AudioPermission | null = null

/**
 * 创建或获取音频权限管理实例
 */
function createAudioPermission(): AudioPermission {
    if (!audioPermissionInstance) {
        audioPermissionInstance = new AudioPermission()
    }
    return audioPermissionInstance
}

export default createAudioPermission